package com.lagou.edu.factory;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Repository;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.annotation.Transactional;
import com.lagou.edu.service.TransferService;
import com.lagou.edu.utils.ReflectionsUtils;
import com.mysql.jdbc.StringUtils;

/***
 * @Author ysc
 * @Description 注解bean工厂
 * @Comment //Comment
 * @Date 6:57 下午 2020/10/4
 * @Param
 * @return
 **/
public class AnnotationBeanFactory
{

    /**
     * 一级，对象缓存
     */
    private final static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    /**
     * 二级，对象缓存
     */
    private final static Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

    /**
     * 根据给定的class类型获取对应的缓存Key
     */
    private final static Map<Class<?>, String> classNameMap = new ConcurrentHashMap<>();

    /**
     * 是否已经初始化过
     */
    private static Boolean hasInit = false;

    private AnnotationBeanFactory()
    {

    }

    /**
     * 初始化方法
     *
     * @throws Exception
     * @author ws 2020年5月12日 上午1:41:19
     * @version 1.0
     */
    public static synchronized void init()
            throws Exception
    {
        if (!hasInit)
        {
            /**
             * 1.先深度遍历出需要管理的实例的类信息，并将他们实例化放入二级缓存中 2.再循环给他们装载Bean信息 很笨很笨的方法……
             */

            Set<Class<?>> clazzSet1 = ReflectionsUtils.getClassesByAnnotaion(Service.class);
            Set<Class<?>> clazzSet2 = ReflectionsUtils.getClassesByAnnotaion(Repository.class);
            clazzSet2.addAll(clazzSet1);
            for (Class<?> clazz : clazzSet2)
            {
                System.out.println("正在生成："+clazz.getName());
                createBean(clazz);
            }
            System.out.println("开始装载属性……");
            for (Class<?> clazz : clazzSet2)
            {
                Class<?>[] interFacesClazz = clazz.getInterfaces();
                String beanId = classNameMap.get(interFacesClazz.length > 0 ? interFacesClazz[0] : clazz);
                Object obj = AnnotationBeanFactory.earlySingletonObjects.get(beanId);
                Set<Field> fieldSet = ReflectionsUtils.getFieldsAnnotatedWith(clazz, Autowired.class);
                if (fieldSet != null&&obj!=null)
                {

                    for (Field field : fieldSet)
                    {
                        populateProp(obj, field);
                    }
                }

                synchronized (AnnotationBeanFactory.singletonObjects)
                {
                    AnnotationBeanFactory.singletonObjects.put(beanId, obj);
                    AnnotationBeanFactory.earlySingletonObjects.remove(beanId);
                }
            }

           hasInit = true;
        }
    }

    /**
     * 创建 bean实例
     *
     * @param clazz 待创建实例的类型
     * @throws Exception
     * @author ws 2020年5月12日 下午10:06:18
     * @version 1.0
     */
    protected static void createBean(Class<?> clazz)
            throws Exception
    {
        Service annotationName = clazz.getAnnotation(Service.class);
        String beanId = annotationName == null ? clazz.getAnnotation(Repository.class).value() : annotationName.value();
        String className = clazz.getName();
        // 驼峰法创建id
        beanId = StringUtils.isNullOrEmpty(beanId) ? underline2Camel(className.substring(className.lastIndexOf(".") + 1, className.length()), true) : beanId;

        // 创建实例后把自己加入到二级缓存，算是提前暴露自己
        Object obj = clazz.newInstance();
        addEarlySingleton(beanId, clazz, obj);
    }

    /**
     * 加入二级缓存
     *
     * @param clazz     类
     * @param singleton 实例
     * @author ws 2020年5月12日 下午10:13:19
     * @version 1.0
     */
    protected static void addEarlySingleton(String beanId, Class<?> clazz, Object singleton)
    {

        synchronized (AnnotationBeanFactory.singletonObjects)
        {
            if (!AnnotationBeanFactory.singletonObjects.containsKey(beanId))
            {
                AnnotationBeanFactory.earlySingletonObjects.put(beanId, singleton);
                Class<?>[] interFacesClazz = clazz.getInterfaces();
                AnnotationBeanFactory.classNameMap.put(interFacesClazz.length > 0 ? interFacesClazz[0] : clazz, beanId);
            }
        }
    }

    /**
     * 为对象装载值
     *
     * @param obj
     * @author ws 2020年5月12日 下午10:20:23
     * @version 1.0
     */
    protected static void populateProp(Object obj, Field field)
            throws Exception
    {
        synchronized (AnnotationBeanFactory.singletonObjects)
        {
            // 待装载的对象信息
            Object propObj = null;
            Class<?>[] interFacesClazz = field.getType().getInterfaces();
            String beanId = classNameMap.get(interFacesClazz.length > 0 ? interFacesClazz[0] : field.getType());
            /**
             * 1.先去一级缓存找没有。找二级 2.二级找不到，说明压根就没创建。于是创建一个对应的对象出来 3.把不成熟的自己放进需要赋值的对象中，从而解决循环依赖
             */
            if (AnnotationBeanFactory.singletonObjects.containsKey(beanId))
            {
                propObj = getBean(field.getType());
            }
            else if (AnnotationBeanFactory.earlySingletonObjects.containsKey(beanId))
            {
                propObj = AnnotationBeanFactory.earlySingletonObjects.get(beanId);
            }
            ReflectionsUtils.setValue(obj, field.getName(), propObj);
        }
    }

    /**
     * 获取bean实例。
     *
     * @param clazz 获取对象类型
     * @return
     * @author ws 2020年5月12日 上午1:40:06
     * @version 1.0
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(Class<T> clazz)
           
    {
        if (classNameMap.containsKey(clazz))
        {
            String beanId = classNameMap.get(clazz);
            Object singletObj = singletonObjects.get(beanId);
            //如果找不到对象，说明没有实现相关结果，那么直接采用cglib来返回需要的bean
            if(singletObj==null)
            {
                String proxyBeanId = classNameMap.get(ProxyFactory.class);
                ProxyFactory proxyFactory = (ProxyFactory) singletonObjects.get(proxyBeanId);

                return (T) proxyFactory.getCglibProxy(singletObj);
            }

            // 检查该对象是否需要使用代理工厂来控制事务
            if (singletObj.getClass().getAnnotation(Transactional.class) != null)
            {
                String proxyBeanId = classNameMap.get(ProxyFactory.class);
                ProxyFactory proxyFactory = (ProxyFactory) singletonObjects.get(proxyBeanId);

                return (T) proxyFactory.getJdkProxy(singletObj);
            }
            else
            {
                return (T) singletObj;
            }

        }
        else
        {
            return null;
        }
    }

    /**
     * 下划线转驼峰法(默认小驼峰)
     *
     * @param line       源字符串
     * @param smallCamel 大小驼峰,是否为小驼峰(驼峰，第一个字符是大写还是小写)
     * @return 转换后的字符串
     */
    public static String underline2Camel(String line, boolean... smallCamel)
    {
        if (line == null || "".equals(line))
        {
            return "";
        }
        StringBuffer sb = new StringBuffer();
        Pattern pattern = Pattern.compile("([A-Za-z\\d]+)(_)?");
        Matcher matcher = pattern.matcher(line);
        // 匹配正则表达式
        while (matcher.find())
        {
            String word = matcher.group();
            // 当是true 或则是空的情况
            if ((smallCamel.length == 0 || smallCamel[0]) && matcher.start() == 0)
            {
                sb.append(Character.toLowerCase(word.charAt(0)));
            }
            else
            {
                sb.append(Character.toUpperCase(word.charAt(0)));
            }

            int index = word.lastIndexOf('_');
            if (index > 0)
            {
                sb.append(word.substring(1, index).toLowerCase());
            }
            else
            {
                sb.append(word.substring(1));
            }
        }
        return sb.toString();
    }

    public static void main(String[] args)
            throws Exception
    {
        AnnotationBeanFactory.init();

        TransferService tfService = AnnotationBeanFactory.getBean(TransferService.class);
        System.out.println(tfService.toString());
    }
}
